#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <cassert>
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <functional>
using namespace std;
#define NUM 20
using task = function<void()>;

#define Download 0
#define Play 1
#define Print 2

void download() {
    cout << "我是子进程：" << getpid() << ", 我正在执行一个下载任务，我正在下载" << endl;
}
void playvideo() { 
    cout << "我是子进程：" << getpid() << ", 我正在执行一个播放视频的任务，我正在播放视频" << endl;
}
void printlog() {
    cout << "我是子进程：" << getpid() << ", 我正在执行一个打印日志的任务，我正在打印日志" << endl;
}

vector<task> tasks; // 任务队列


void Init_tasks(vector<task> *ptask) {
    ptask->push_back(download);
    ptask->push_back(playvideo);
    ptask->push_back(printlog);
}


static int channelid = 1;
// 定义管道结构体
struct channel {
    string name;
    int crtfd;
    pid_t workid;
    // 构造
    channel(int fd, int id) 
        :crtfd(fd)
        ,workid(id)
    {
        name = "channel" + to_string(channelid++);
    }
};

// 子进程执行任务
void work() {
    while (true) {
        // cout << "work()" << endl;
        int cmmd = 0;
        ssize_t ret = read(0, &cmmd, sizeof(cmmd));
        if (ret == 4) {
            // 执行任务
            tasks[cmmd]();
        } else if (ret == 0) { // 如果写端关闭，read会返回0，因为这里只有一个父进程写端，所以如果父进程退出了，子进程再读也就没有意义了，直接退出
            break;
        } else {
        }
    }
    cout << "work done……" << endl;

}

// 父进程选择管道(子进程)，选择任务让子进程执行，缺省参数表示默认一直执行下去
void sandcmmd(const vector<channel> &ch, bool always_loop = true, int cnt = -1) {
    int pos = 0;
    while (true) {
        if (!always_loop) {
            cnt--;
            if (cnt <= 0) {
                break;
            }
        }
        // 生成随机数，选择任务
        int taski = rand() % tasks.size();

        // 轮询的选择子进程执行任务
        int fd = ch[pos++].crtfd;
        pos %= ch.size();

        // 发送任务
        int ret = write(fd, &taski, sizeof(taski));
        
        sleep(1);

    }
    cout << "sandcmmd done……" << endl;

}


// 定义并创建管管道
void CreateChannel(vector<channel> *ch) {
    vector<int> old;
    for (int i = 0; i < NUM; i++) {
        // 创建管道
        int pipefd[2] = { 0 };
        int ret = pipe(pipefd);
        assert(ret == 0);

        // 创建进程
        pid_t id = fork();
        assert(id != -1);
        if (id == 0) {
            if (!old.empty()) {
                for (auto x : old) {
                    close(x);
                }
            }
            // child
            close(pipefd[1]); // 子进程读，子进程关闭写端
            // 读取父进程发来的任务，并执行
            dup2(pipefd[0], 0); // 输入重定向
            work();


            exit(0);
        }
        
        // father
        close(pipefd[0]); // 父进程写，父进程关闭读端

        ch->push_back(channel(pipefd[1], id));
        old.push_back(pipefd[1]);
        }

}

void ReleaseChannels(vector<channel>& ch) {
    for (const auto& c : ch) {
        close(c.crtfd);
        pid_t rid = waitpid(c.workid, nullptr, 0);
        if (rid == c.workid) {
            cout << "wait child process : " << rid << "sucess……" << endl;
        }
    }
    // for (const auto& c : ch) {
    //     pid_t rid = waitpid(c.workid, nullptr, 0);
    //     if (rid == c.workid) {
    //         cout << "wait child process : " << rid << "sucess……" << endl;
    //     }
    // }
}

int main() {
    // 创建进程池步骤
    // 1、定义并创建管道
    // 2、创建进程
    // 3、构建单向通信的信道(子进程读，父进程写)
    // 4、管理每个信道(创建结构体，创建结构体数组)
    // 5、开始完成任务(分配任务，使用包装器包装任务列表，父进程选择任务，选择信道(进程)，发送任务)
    // 6、回收子进程，并关闭信道
    srand(time(NULL) * getpid()); // 种下随机数种子
    vector<channel> channels; // 管道队列
    Init_tasks(&tasks); // 初始化任务队列
    // 定义并创建管道
    CreateChannel(&channels);

    // 选择管道(进程)，选择进程返送任务
    sandcmmd(channels, false, 10);

    for (const auto& ch : channels) {
        cout << ch.name << " " << ch.crtfd << " " << ch.workid << endl;
    }
    // 回收资源，等待子进程，关闭管道
    ReleaseChannels(channels);
    return 0;
}
